package sfeir.gwt.ergosoom.server.guice;

import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.Finder;
import org.restlet.resource.ServerResource;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;

/**
 * A Guice module that doubles as a factory for creating Finders that look up
 * Handler or Resource instances from a given class or Guice Key. On first use
 * of these Finders, if the module hasn't been used to create an Injector, this
 * module creates its own Injector.
 */
public class FinderFactoryModule extends AbstractModule implements FinderFactory {

    private final Module[] moduleArray;
    @Inject
    private volatile Injector injector;

    @Inject
    private void clearAlreadyBound() {
        alreadyBound.set(false);
    }

    private static ThreadLocal<Boolean> alreadyBound = new ThreadLocal<Boolean>() {
        protected Boolean initialValue() {
            return false;
        }
    };

    /**
     * Creates a FinderFactoryModule that will install the given modules.
     */
    public FinderFactoryModule(Module... modules) {
        this.moduleArray = modules;
    }

    //
    // FinderFactory methods
    //

    public Finder finderFor(Key<? extends ServerResource> key) {
        return new KeyFinder(key);
    }

    public Finder finderFor(Class<? extends ServerResource> cls) {
        return new KeyFinder(Key.get(cls));
    }

    /**
     * This method must be called by any method that overrides it.
     */
    @Override
    protected final void configure() {

        if (injector != null) {
            throw new IllegalStateException("can't reconfigure with existing Injector");
        }

        if (!alreadyBound.get()) {
            alreadyBound.set(true);

            // bind(Context.class).toProvider(new Provider<Context>() {
            // public Context get() {
            // return Context.getCurrent();
            // }
            // });
            // bind(Request.class).toProvider(new Provider<Request>() {
            // public Request get() {
            // return Request.getCurrent();
            // }
            // });
            // bind(Response.class).toProvider(new Provider<Response>() {
            // public Response get() {
            // return Response.getCurrent();
            // }
            // });
            bind(FinderFactory.class).toInstance(this);
        }

        for (Module module : moduleArray)
            install(module);
    }

    class KeyFinder extends Finder {
        private final Key<? extends ServerResource> key;

        KeyFinder(Key<? extends ServerResource> key) {
            this.key = key;
        }

        @Override
        public java.lang.Class<?> getTargetClass() {
            return ServerResource.class;
        };

        @Override
        public ServerResource find(Request request, Response response) {
            return getInjector().getInstance(key);
        }

        private Injector getInjector() {
            Injector inj = injector;
            if (inj == null) {
                synchronized (FinderFactoryModule.this) {
                    inj = injector;
                    if (inj == null) {
                        inj = Guice.createInjector(FinderFactoryModule.this);
                    }
                }
            }
            return inj;
        }
    }
}